package com.lxw.multidb.modules.sql;

import cn.tannn.jdevelops.exception.built.BusinessException;
import com.lxw.multidb.controller.dataharvest.dto.FieldRule;
import com.lxw.multidb.controller.dataharvest.dto.HarvestDbDTO;
import com.lxw.multidb.controller.dto.ColumnInfo;
import com.lxw.multidb.controller.dto.DbConnectionRequest;
import com.lxw.multidb.modules.sql.entity.DbConnection;
import com.lxw.multidb.modules.sql.service.DbConnectionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Clob;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据收割服务
 * <p>
 * 功能：从源数据库查询数据，经过字段映射和类型转换后，批量插入到目标数据库
 * </p>
 * <p>
 * 主要特性：
 * <ul>
 *   <li>支持多种数据库类型（MySQL、PostgreSQL、SQLServer、Oracle）</li>
 *   <li>分页查询 + 批量插入，优化大数据量迁移性能</li>
 *   <li>支持字段类型自动转换（根据目标表元数据自动推断类型）</li>
 *   <li>支持新建目标表（自动根据源表结构创建）</li>
 *   <li>异步执行，不阻塞主线程</li>
 * </ul>
 * </p>
 *
 * @author lxw
 * @version 2.3
 * @date 2025-07-11
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class HarvestDbService {

    private final DbConnectionService dbConnectionService;
    private final DatabaseMetaService databaseMetaService;

    /**
     * 批量插入的批次大小
     * <p>每次执行batchUpdate的记录数，避免单次批量过大</p>
     */
    private static final int BATCH_SIZE = 500;

    /**
     * 分页查询的页大小
     * <p>每次从源数据库查询的记录数</p>
     */
    private static final int PAGE_SIZE = 1000;

    /**
     * 常用日期格式列表，用于自动解析日期字符串
     */
    private static final String[] DATE_PATTERNS = {
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd HH:mm:ss.SSS",
            "yyyy-MM-dd",
            "yyyy/MM/dd HH:mm:ss",
            "yyyy/MM/dd",
            "yyyyMMdd",
            "yyyyMMddHHmmss"
    };

    /**
     * 异步执行数据收割任务
     * <p>
     * 主要流程：
     * <ol>
     *   <li>获取源数据库和目标数据库连接配置</li>
     *   <li>创建数据源和JdbcTemplate</li>
     *   <li>根据tgtNewTable参数决定是新建表还是追加数据</li>
     *   <li>获取目标表字段元数据，组装完整的字段规则</li>
     *   <li>统计源表数据总量</li>
     *   <li>分页查询源数据</li>
     *   <li>根据字段规则进行类型转换</li>
     *   <li>批量插入目标表</li>
     * </ol>
     * </p>
     *
     * @param dto 数据收割参数对象
     */
    @Async("applicationTaskExecutor")
    public void saveDbHarvestInfo(HarvestDbDTO dto) {
        long startTime = System.currentTimeMillis();

        try {
            // ==================== 1. 获取数据库连接配置 ====================
            DbConnection srcDbConn = dbConnectionService.getById(dto.getSrcDbId())
                    .orElseThrow(() -> new BusinessException("源数据库信息未找到，请重新选择或添加有效数据库配置"));

            DbConnection tgtDbConn = dbConnectionService.getById(dto.getTgtDbId())
                    .orElseThrow(() -> new BusinessException("目标数据库信息未找到，请重新选择或添加有效数据库配置"));

            // ==================== 2. 创建数据源连接 ====================
            // 源数据库数据源
            DbConnectionRequest srcConnectionRequest = srcDbConn.to(DbConnectionRequest.class);
            srcConnectionRequest.setDatabaseName(dto.getSrcDatabase());
            srcConnectionRequest.setTableName(dto.getSrcTableName());
            DataSource srcDataSource = databaseMetaService.createDataSource(srcConnectionRequest);
            JdbcTemplate srcJdbcTemplate = new JdbcTemplate(srcDataSource);

            // 目标数据库数据源
            DbConnectionRequest tgtConnectionRequest = tgtDbConn.to(DbConnectionRequest.class);
            tgtConnectionRequest.setDatabaseName(dto.getTgtDatabase());
            tgtConnectionRequest.setTableName(dto.getTgtTableName());
            DataSource tgtDataSource = databaseMetaService.createDataSource(tgtConnectionRequest);
            JdbcTemplate tgtJdbcTemplate = new JdbcTemplate(tgtDataSource);

            // ==================== 3. 根据tgtNewTable参数处理目标表 ====================
            List<FieldRule> fieldRules;
            boolean tgtNewTable = dto.isTgtNewTable();

            if (tgtNewTable) {
                // -------------------- 3.1 新建目标表模式 --------------------
                log.info("===> 新建目标表模式：源表[{}] -> 目标表[{}]", dto.getSrcTableName(), dto.getTgtTableName());

                // 3.1.1 查询来源表字段信息
                List<ColumnInfo> srcColumnInfos = databaseMetaService.listColumnsWithMeta(srcConnectionRequest);
                if (srcColumnInfos == null || srcColumnInfos.isEmpty()) {
                    throw new BusinessException("源表字段信息为空，无法创建目标表");
                }
                log.info("===> 获取源表[{}]字段元数据，共{}个字段", dto.getSrcTableName(), srcColumnInfos.size());

                // 3.1.2 根据传入的 fieldRules 构建源字段到目标字段的映射
                Map<String, String> sourceToTargetFieldMap = buildSourceToTargetFieldMap(dto.getFieldRules());

                // 3.1.3 根据映射筛选并转换源表字段信息
                List<ColumnInfo> targetColumnInfos = filterAndMapColumnInfos(srcColumnInfos, sourceToTargetFieldMap);
                if (targetColumnInfos.isEmpty()) {
                    throw new BusinessException("根据字段映射规则筛选后，没有可用的字段");
                }

                // 3.1.4 创建目标表（如果存在则先删除）
                createTargetTable(tgtJdbcTemplate, tgtDbConn.getDbType(), dto.getTgtTableName(), targetColumnInfos);
                log.info("===> 目标表[{}]创建成功", dto.getTgtTableName());

                // 3.1.5 组装完整的 List<FieldRule>（根据源表字段信息补充 type 和 length）
                fieldRules = buildFieldRulesFromColumnInfos(dto.getFieldRules(), srcColumnInfos);
                log.info("===> 字段规则组装完成（新建表模式），共{}个映射字段", fieldRules.size());

            } else {
                // -------------------- 3.2 追加数据模式 --------------------
                log.info("===> 追加数据模式：源表[{}] -> 目标表[{}]", dto.getSrcTableName(), dto.getTgtTableName());

                // 预先获取目标表字段信息，便于后续校验和转换
                List<ColumnInfo> columnInfos = databaseMetaService.listColumnsWithMeta(tgtConnectionRequest);
                log.info("===> 获取目标表[{}]字段元数据，共{}个字段", dto.getTgtTableName(), columnInfos.size());

                // 根据 columnInfos 组装完整的 List<FieldRule>（补充 type 和 length）
                fieldRules = enrichFieldRulesWithColumnInfo(dto.getFieldRules(), columnInfos);
                log.info("===> 字段规则组装完成（追加模式），共{}个映射字段", fieldRules.size());
            }

            // ==================== 4. 查询源表总记录数 ====================
            String sourceTableName = dto.getSrcTableName();
            String condition = dto.getSrcCondition();

            String countSql = buildCountSql(sourceTableName, condition);
            Long totalCount = srcJdbcTemplate.queryForObject(countSql, Long.class);

            if (totalCount == null || totalCount <= 0) {
                log.info("本次无数据需要收割，源表：{}", sourceTableName);
                return;
            }
            log.info("===> 数据库收割任务开始，源表：{}，总记录数：{}", sourceTableName, totalCount);

            // ==================== 5. 构建查询SQL和插入SQL ====================
            String sortField = StringUtils.isNotBlank(dto.getSrcSortField()) ? dto.getSrcSortField() : "id";

            // 构建字段规则映射（目标字段名 -> 字段规则），便于后续类型转换查找
            Map<String, FieldRule> fieldRuleMap = buildFieldRuleMap(fieldRules);

            // 目标字段列表
            List<String> targetColumns = new ArrayList<>();
            for (FieldRule fieldRule : fieldRules) {
                targetColumns.add(fieldRule.getField());
            }

            // 构建查询SQL的基础部分
            String baseQuerySql = buildBaseQuerySql(sortField, fieldRules, sourceTableName, condition);

            // 构建插入SQL
            String insertSql = buildInsertSql(dto.getTgtTableName(), targetColumns);
            log.debug("===> 插入SQL: {}", insertSql);

            // ==================== 6. 分页查询并批量插入 ====================
            int totalPage = (int) Math.ceil(totalCount / (double) PAGE_SIZE);
            long insertedCount = 0;

            for (int page = 0; page < totalPage; page++) {
                int offset = page * PAGE_SIZE;

                // 构建分页查询SQL
                String querySql = buildPaginatedSql(
                        srcDbConn.getDbType(),
                        sortField,
                        dto.getSrcSortType(),
                        baseQuerySql,
                        offset,
                        PAGE_SIZE
                );

                log.debug("===> 第{}页分页查询SQL: {}", page + 1, querySql);

                // 执行查询
                List<Map<String, Object>> dataList = srcJdbcTemplate.queryForList(querySql);

                // 如果当前页为空，说明后续页也没有数据了，直接break结束循环
                if (dataList.isEmpty()) {
                    log.info("===> 第{}页查询结果为空，提前结束分页查询", page + 1);
                    break;
                }

                // 批量插入数据
                int inserted = batchInsert(tgtJdbcTemplate, insertSql, dataList, targetColumns, fieldRuleMap);
                insertedCount += inserted;

                log.info("===> 第{}页处理完成，本页查询{}条，插入{}条，累计插入{}条",
                        page + 1, dataList.size(), inserted, insertedCount);
            }

            long endTime = System.currentTimeMillis();
            log.info("===> 数据库收割任务完成，总插入{}条记录，耗时{}ms", insertedCount, (endTime - startTime));

        } catch (BusinessException e) {
            // 业务异常直接抛出
            log.error("===> 数据库收割任务失败（业务异常）：{}", e.getMessage());
            throw e;
        } catch (Exception e) {
            // 其他异常包装后抛出
            log.error("===> 数据库收割任务失败（系统异常）：{}", e.getMessage(), e);
            throw new BusinessException("数据收割执行失败：" + e.getMessage());
        }
    }

    // ==================== 新建表相关方法 ====================

    /**
     * 构建源字段到目标字段的映射关系
     * <p>
     * 将传入的 fieldRules 转换为 Map，key 为源字段名（小写），value 为目标字段名
     * </p>
     *
     * @param fieldRules 字段规则列表
     * @return 源字段名 -> 目标字段名 的映射
     */
    private Map<String, String> buildSourceToTargetFieldMap(List<FieldRule> fieldRules) {
        if (fieldRules == null || fieldRules.isEmpty()) {
            return new HashMap<>();
        }
        Map<String, String> map = new HashMap<>(fieldRules.size());
        for (FieldRule rule : fieldRules) {
            if (StringUtils.isNotBlank(rule.getSourceField()) && StringUtils.isNotBlank(rule.getField())) {
                // 使用小写作为key，便于不区分大小写匹配
                map.put(rule.getSourceField().toLowerCase(), rule.getField());
            }
        }
        return map;
    }

    /**
     * 根据映射关系筛选并转换源表字段信息为目标表字段信息
     * <p>
     * 只保留在映射关系中存在的字段，并将字段名替换为目标字段名
     * </p>
     *
     * @param srcColumnInfos         源表字段信息列表
     * @param sourceToTargetFieldMap 源字段名 -> 目标字段名 的映射
     * @return 转换后的目标表字段信息列表
     */
    private List<ColumnInfo> filterAndMapColumnInfos(List<ColumnInfo> srcColumnInfos,
                                                     Map<String, String> sourceToTargetFieldMap) {
        List<ColumnInfo> targetColumnInfos = new ArrayList<>();

        for (ColumnInfo srcColumn : srcColumnInfos) {
            String srcFieldName = srcColumn.getName().toLowerCase();
            String targetFieldName = sourceToTargetFieldMap.get(srcFieldName);

            if (targetFieldName != null) {
                // 复制源字段信息，修改字段名为目标字段名
                ColumnInfo targetColumn = new ColumnInfo();
                targetColumn.setName(targetFieldName);
                targetColumn.setDataType(srcColumn.getDataType());
                targetColumn.setTypeName(srcColumn.getTypeName());
                targetColumn.setColumnSize(srcColumn.getColumnSize());
                targetColumn.setDecimalDigits(srcColumn.getDecimalDigits());
                targetColumn.setNullable(srcColumn.isNullable());
                targetColumn.setRemarks(srcColumn.getRemarks());
                targetColumnInfos.add(targetColumn);
            }
        }

        return targetColumnInfos;
    }

    /**
     * 创建目标表
     * <p>
     * 处理流程：
     * <ol>
     *   <li>如果目标表已存在，先删除</li>
     *   <li>根据数据库类型生成对应的建表DDL语句</li>
     *   <li>执行建表语句</li>
     * </ol>
     * </p>
     * <p>
     * 建表规则：
     * <ul>
     *   <li>自动添加 id 字段作为自增长主键</li>
     *   <li>其余字段根据 columnInfos 中的类型信息创建</li>
     * </ul>
     * </p>
     *
     * @param jdbcTemplate JDBC模板
     * @param dbType       数据库类型
     * @param tableName    目标表名
     * @param columnInfos  字段信息列表（已经是目标字段名）
     */
    private void createTargetTable(JdbcTemplate jdbcTemplate, String dbType,
                                   String tableName, List<ColumnInfo> columnInfos) {
        String dbTypeLower = dbType.toLowerCase();

        // 1. 删除已存在的表
        String dropSql = buildDropTableSql(dbTypeLower, tableName);
        try {
            jdbcTemplate.execute(dropSql);
            log.info("===> 已删除已存在的表[{}]", tableName);
        } catch (Exception e) {
            // 表不存在时会抛出异常，忽略即可
            log.debug("===> 表[{}]不存在或删除时发生错误，继续创建新表", tableName);
        }

        // 2. 构建建表语句
        String createSql = buildCreateTableSql(dbTypeLower, tableName, columnInfos);
        log.debug("===> 建表SQL: {}", createSql);

        // 3. 执行建表语句
        try {
            jdbcTemplate.execute(createSql);
        } catch (Exception e) {
            log.error("===> 创建表[{}]失败: {}", tableName, e.getMessage());
            throw new BusinessException("创建目标表失败：" + e.getMessage());
        }
    }

    /**
     * 构建删除表的SQL语句
     *
     * @param dbType    数据库类型（小写）
     * @param tableName 表名
     * @return 删除表的SQL语句
     */
    private String buildDropTableSql(String dbType, String tableName) {
        return switch (dbType) {
            case "mysql", "mariadb", "postgresql", "postgres", "dm", "dameng", "kingbase" ->
                    "DROP TABLE IF EXISTS " + tableName;
            case "sqlserver", "mssql" -> "IF OBJECT_ID('" + tableName + "', 'U') IS NOT NULL DROP TABLE " + tableName;
            case "oracle" ->
                // Oracle没有直接的 IF EXISTS 语法，使用PL/SQL块
                    "BEGIN EXECUTE IMMEDIATE 'DROP TABLE " + tableName + "'; EXCEPTION WHEN OTHERS THEN NULL; END;";
            default -> "DROP TABLE IF EXISTS " + tableName;
        };
    }

    /**
     * 构建建表SQL语句
     * <p>
     * 根据不同数据库类型生成对应的DDL语句：
     * <ul>
     *   <li>MySQL/MariaDB: AUTO_INCREMENT</li>
     *   <li>PostgreSQL/Kingbase: SERIAL</li>
     *   <li>SQL Server: IDENTITY</li>
     *   <li>Oracle: GENERATED ALWAYS AS IDENTITY</li>
     *   <li>达梦(DM): IDENTITY</li>
     * </ul>
     * </p>
     *
     * @param dbType      数据库类型（小写）
     * @param tableName   表名
     * @param columnInfos 字段信息列表
     * @return 建表SQL语句
     */
    private String buildCreateTableSql(String dbType, String tableName, List<ColumnInfo> columnInfos) {
        StringBuilder sql = new StringBuilder();
        sql.append("CREATE TABLE ").append(tableName).append(" (\n");

        // 添加自增主键 id
        String idColumnDef = buildIdColumnDefinition(dbType);
        sql.append("    ").append(idColumnDef).append(",\n");

        // 添加其余字段（排除id字段，避免重复）
        List<String> columnDefs = new ArrayList<>();
        for (ColumnInfo column : columnInfos) {
            // 跳过 id 字段，因为已经手动添加
            if ("id".equalsIgnoreCase(column.getName())) {
                continue;
            }
            String columnDef = buildColumnDefinition(dbType, column);
            columnDefs.add("    " + columnDef);
        }

        sql.append(String.join(",\n", columnDefs));

        // 添加主键约束
        sql.append(",\n    PRIMARY KEY (id)");

        sql.append("\n)");

        // MySQL/MariaDB 添加引擎和字符集
        if ("mysql".equals(dbType) || "mariadb".equals(dbType)) {
            sql.append(" ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
        }

        return sql.toString();
    }

    /**
     * 构建自增主键 id 的列定义
     *
     * @param dbType 数据库类型（小写）
     * @return id 列的定义
     */
    private String buildIdColumnDefinition(String dbType) {
        return switch (dbType) {
            case "mysql", "mariadb" -> "id BIGINT NOT NULL AUTO_INCREMENT";
            case "postgresql", "postgres", "kingbase" -> "id BIGSERIAL NOT NULL";
            case "sqlserver", "mssql" -> "id BIGINT NOT NULL IDENTITY(1,1)";
            case "oracle" -> "id NUMBER(19) GENERATED ALWAYS AS IDENTITY NOT NULL";
            case "dm", "dameng" -> "id BIGINT NOT NULL IDENTITY(1,1)";
            default -> "id BIGINT NOT NULL AUTO_INCREMENT";
        };
    }

    /**
     * 构建单个字段的列定义
     * <p>
     * 根据 ColumnInfo 中的类型信息，生成对应数据库的列定义语句
     * </p>
     *
     * @param dbType 数据库类型（小写）
     * @param column 字段信息
     * @return 列定义语句（如：username VARCHAR(255) NOT NULL）
     */
    private String buildColumnDefinition(String dbType, ColumnInfo column) {
        StringBuilder def = new StringBuilder();
        def.append(column.getName()).append(" ");

        // 转换数据类型
        String columnType = convertToTargetDbType(dbType, column);
        def.append(columnType);

        // 添加 NULL/NOT NULL 约束
        if (!column.isNullable()) {
            def.append(" NOT NULL");
        } else {
            def.append(" NULL");
        }

        return def.toString();
    }

    /**
     * 将源数据库的类型转换为目标数据库的类型
     * <p>
     * 根据 JDBC 类型和类型名称，映射到目标数据库支持的类型
     * </p>
     *
     * @param dbType 目标数据库类型（小写）
     * @param column 源字段信息
     * @return 目标数据库的类型定义（如 VARCHAR(255)）
     */
    private String convertToTargetDbType(String dbType, ColumnInfo column) {
        String typeName = column.getTypeName() != null ? column.getTypeName().toLowerCase() : "";
        int jdbcType = column.getDataType();
        Integer columnSize = column.getColumnSize();
        Integer decimalDigits = column.getDecimalDigits();

        // 根据类型名称或 JDBC 类型确定标准类型
        String standardType = mapToStandardType(jdbcType, column.getTypeName(),
                columnSize != null ? columnSize : 0);

        // 根据标准类型转换为目标数据库的类型
        return convertStandardTypeToDbType(dbType, standardType, columnSize, decimalDigits);
    }

    /**
     * 将标准类型转换为具体数据库的类型定义
     *
     * @param dbType        目标数据库类型
     * @param standardType  标准类型（如 varchar、bigint、datetime 等）
     * @param columnSize    列大小
     * @param decimalDigits 小数位数
     * @return 数据库类型定义
     */
    private String convertStandardTypeToDbType(String dbType, String standardType,
                                               Integer columnSize, Integer decimalDigits) {
        // 设置默认值
        int size = (columnSize != null && columnSize > 0) ? columnSize : 255;
        int digits = (decimalDigits != null && decimalDigits > 0) ? decimalDigits : 2;

        return switch (standardType.toLowerCase()) {
            case "bigint" -> getBigintType(dbType);
            case "int", "integer" -> getIntType(dbType);
            case "tinyint" -> getTinyintType(dbType);
            case "varchar" -> getVarcharType(dbType, size);
            case "char" -> getCharType(dbType, size);
            case "text" -> getTextType(dbType);
            case "longtext", "mediumtext" -> getLongTextType(dbType);
            case "date" -> getDateType(dbType);
            case "datetime", "timestamp" -> getDatetimeType(dbType);
            case "decimal", "numeric" -> getDecimalType(dbType, size, digits);
            case "double" -> getDoubleType(dbType);
            case "float" -> getFloatType(dbType);
            case "boolean", "bool" -> getBooleanType(dbType);
            default -> "VARCHAR(255)"; // 未知类型默认使用 VARCHAR
        };
    }

    /**
     * 获取 BIGINT 类型定义
     */
    private String getBigintType(String dbType) {
        return switch (dbType) {
            case "oracle" -> "NUMBER(19)";
            default -> "BIGINT";
        };
    }

    /**
     * 获取 INT 类型定义
     */
    private String getIntType(String dbType) {
        return switch (dbType) {
            case "oracle" -> "NUMBER(10)";
            default -> "INT";
        };
    }

    /**
     * 获取 TINYINT 类型定义
     */
    private String getTinyintType(String dbType) {
        return switch (dbType) {
            case "oracle" -> "NUMBER(3)";
            case "postgresql", "postgres", "kingbase" -> "SMALLINT";
            default -> "TINYINT";
        };
    }

    /**
     * 获取 VARCHAR 类型定义
     */
    private String getVarcharType(String dbType, int size) {
        // 限制最大长度
        int maxSize = switch (dbType) {
            case "oracle" -> Math.min(size, 4000);
            case "sqlserver", "mssql" -> Math.min(size, 8000);
            default -> Math.min(size, 65535);
        };
        return "VARCHAR(" + maxSize + ")";
    }

    /**
     * 获取 CHAR 类型定义
     */
    private String getCharType(String dbType, int size) {
        int maxSize = Math.min(size, 255);
        return "CHAR(" + maxSize + ")";
    }

    /**
     * 获取 TEXT 类型定义
     */
    private String getTextType(String dbType) {
        return switch (dbType) {
            case "oracle" -> "CLOB";
            case "sqlserver", "mssql" -> "NVARCHAR(MAX)";
            default -> "TEXT";
        };
    }

    /**
     * 获取 LONGTEXT 类型定义
     */
    private String getLongTextType(String dbType) {
        return switch (dbType) {
            case "oracle" -> "CLOB";
            case "sqlserver", "mssql" -> "NVARCHAR(MAX)";
            case "postgresql", "postgres", "kingbase" -> "TEXT";
            default -> "LONGTEXT";
        };
    }

    /**
     * 获取 DATE 类型定义
     */
    private String getDateType(String dbType) {
        return "DATE";
    }

    /**
     * 获取 DATETIME 类型定义
     */
    private String getDatetimeType(String dbType) {
        return switch (dbType) {
            case "oracle" -> "TIMESTAMP";
            case "postgresql", "postgres", "kingbase" -> "TIMESTAMP";
            default -> "DATETIME";
        };
    }

    /**
     * 获取 DECIMAL 类型定义
     */
    private String getDecimalType(String dbType, int precision, int scale) {
        int p = Math.min(precision, 38);
        int s = Math.min(scale, p);
        return switch (dbType) {
            case "oracle" -> "NUMBER(" + p + "," + s + ")";
            default -> "DECIMAL(" + p + "," + s + ")";
        };
    }

    /**
     * 获取 DOUBLE 类型定义
     */
    private String getDoubleType(String dbType) {
        return switch (dbType) {
            case "oracle" -> "BINARY_DOUBLE";
            case "sqlserver", "mssql" -> "FLOAT";
            default -> "DOUBLE";
        };
    }

    /**
     * 获取 FLOAT 类型定义
     */
    private String getFloatType(String dbType) {
        return switch (dbType) {
            case "oracle" -> "BINARY_FLOAT";
            default -> "FLOAT";
        };
    }

    /**
     * 获取 BOOLEAN 类型定义
     */
    private String getBooleanType(String dbType) {
        return switch (dbType) {
            case "oracle" -> "NUMBER(1)";
            case "sqlserver", "mssql" -> "BIT";
            case "postgresql", "postgres", "kingbase" -> "BOOLEAN";
            default -> "TINYINT(1)";
        };
    }

    /**
     * 根据源表字段信息构建完整的字段规则列表（用于新建表模式）
     * <p>
     * 根据源表的 columnInfos 补充 type、length、name 等信息
     * </p>
     *
     * @param inputFieldRules 传入的字段规则列表（包含 sourceField 和 field 映射）
     * @param srcColumnInfos  源表字段元数据列表
     * @return 补充完整的字段规则列表
     */
    private List<FieldRule> buildFieldRulesFromColumnInfos(List<FieldRule> inputFieldRules,
                                                           List<ColumnInfo> srcColumnInfos) {
        if (inputFieldRules == null || inputFieldRules.isEmpty()) {
            throw new BusinessException("字段映射规则不能为空");
        }

        // 将源表 columnInfos 转换为 Map，key 为字段名（小写）
        Map<String, ColumnInfo> srcColumnInfoMap = new HashMap<>(srcColumnInfos.size());
        for (ColumnInfo columnInfo : srcColumnInfos) {
            srcColumnInfoMap.put(columnInfo.getName().toLowerCase(), columnInfo);
        }

        List<FieldRule> enrichedFieldRules = new ArrayList<>(inputFieldRules.size());

        for (FieldRule inputRule : inputFieldRules) {
            // 创建新的 FieldRule，复制基本信息
            FieldRule enrichedRule = new FieldRule();
            enrichedRule.setSourceField(inputRule.getSourceField());
            enrichedRule.setField(inputRule.getField());

            // 根据源字段名查找 ColumnInfo
            String sourceFieldName = inputRule.getSourceField();
            ColumnInfo columnInfo = srcColumnInfoMap.get(sourceFieldName.toLowerCase());

            if (columnInfo != null) {
                // 根据源表 ColumnInfo 设置类型信息
                String standardType = mapToStandardType(
                        columnInfo.getDataType(),
                        columnInfo.getTypeName(),
                        columnInfo.getColumnSize() != null ? columnInfo.getColumnSize() : 0
                );
                enrichedRule.setType(standardType);

                // 设置字段长度
                enrichedRule.setLength(columnInfo.getColumnSize());

                // 设置字段名称（使用备注，如果为空则使用目标字段名）
                String fieldName = StringUtils.isNotBlank(columnInfo.getRemarks())
                        ? columnInfo.getRemarks()
                        : inputRule.getField();
                enrichedRule.setName(fieldName);

                log.debug("===> 字段[{}->{}]类型推断: typeName={}, dataType={}, 标准类型={}, 长度={}",
                        sourceFieldName, inputRule.getField(), columnInfo.getTypeName(),
                        columnInfo.getDataType(), standardType, columnInfo.getColumnSize());
            } else {
                // 源表中未找到对应字段，记录警告但不中断
                log.warn("===> 源表中未找到字段[{}]，将不进行类型转换", sourceFieldName);
                // 保留原有的type和length（如果有的话）
                enrichedRule.setType(inputRule.getType());
                enrichedRule.setLength(inputRule.getLength());
                enrichedRule.setName(inputRule.getName());
            }

            enrichedFieldRules.add(enrichedRule);
        }

        return enrichedFieldRules;
    }

    // ==================== 追加数据模式相关方法 ====================

    /**
     * 根据目标表字段元数据，组装完整的字段规则列表
     * <p>
     * 传入的 fieldRules 只包含 sourceField 和 field（目标字段名），
     * 本方法根据目标表的 columnInfos 补充 type、length、name 等信息。
     * </p>
     * <p>
     * 处理逻辑：
     * <ol>
     *   <li>将 columnInfos 转换为 Map（字段名 -> ColumnInfo），便于快速查找</li>
     *   <li>遍历传入的 fieldRules，根据目标字段名（field）查找对应的 ColumnInfo</li>
     *   <li>根据 ColumnInfo 的 typeName 和 dataType 推断标准化的类型名称</li>
     *   <li>设置字段长度（columnSize）</li>
     *   <li>设置字段备注为 name</li>
     * </ol>
     * </p>
     *
     * @param inputFieldRules 传入的字段规则列表（仅包含 sourceField 和 field）
     * @param columnInfos     目标表字段元数据列表
     * @return 补充完整的字段规则列表
     */
    private List<FieldRule> enrichFieldRulesWithColumnInfo(List<FieldRule> inputFieldRules,
                                                           List<ColumnInfo> columnInfos) {
        if (inputFieldRules == null || inputFieldRules.isEmpty()) {
            throw new BusinessException("字段映射规则不能为空");
        }

        // 将 columnInfos 转换为 Map，key 为字段名（忽略大小写）
        Map<String, ColumnInfo> columnInfoMap = new HashMap<>(columnInfos.size());
        for (ColumnInfo columnInfo : columnInfos) {
            // 使用小写作为key，便于不区分大小写匹配
            columnInfoMap.put(columnInfo.getName().toLowerCase(), columnInfo);
        }

        List<FieldRule> enrichedFieldRules = new ArrayList<>(inputFieldRules.size());

        for (FieldRule inputRule : inputFieldRules) {
            // 创建新的 FieldRule，复制基本信息
            FieldRule enrichedRule = new FieldRule();
            enrichedRule.setSourceField(inputRule.getSourceField());
            enrichedRule.setField(inputRule.getField());

            // 根据目标字段名查找 ColumnInfo
            String targetFieldName = inputRule.getField();
            ColumnInfo columnInfo = columnInfoMap.get(targetFieldName.toLowerCase());

            if (columnInfo != null) {
                // 根据 ColumnInfo 设置类型信息（传入 columnSize 用于区分 varchar 和 varchar(max) 等）
                String standardType = mapToStandardType(
                        columnInfo.getDataType(),
                        columnInfo.getTypeName(),
                        columnInfo.getColumnSize() != null ? columnInfo.getColumnSize() : 0
                );
                enrichedRule.setType(standardType);

                // 设置字段长度
                enrichedRule.setLength(columnInfo.getColumnSize());

                // 设置字段名称（使用备注，如果为空则使用字段名）
                String fieldName = StringUtils.isNotBlank(columnInfo.getRemarks())
                        ? columnInfo.getRemarks()
                        : columnInfo.getName();
                enrichedRule.setName(fieldName);

                log.debug("===> 字段[{}]类型推断: typeName={}, dataType={}, 标准类型={}, 长度={}",
                        targetFieldName, columnInfo.getTypeName(), columnInfo.getDataType(),
                        standardType, columnInfo.getColumnSize());
            } else {
                // 目标表中未找到对应字段，记录警告但不中断
                log.warn("===> 目标表中未找到字段[{}]，将不进行类型转换", targetFieldName);
                // 保留原有的type和length（如果有的话）
                enrichedRule.setType(inputRule.getType());
                enrichedRule.setLength(inputRule.getLength());
                enrichedRule.setName(inputRule.getName());
            }

            enrichedFieldRules.add(enrichedRule);
        }

        return enrichedFieldRules;
    }

    // ==================== 类型映射方法 ====================

    /**
     * 将 JDBC 数据类型（java.sql.Types）映射为标准化的类型名称
     * <p>
     * 映射规则基于 java.sql.Types 常量，同时参考数据库特定的类型名称和字段长度。
     * 返回的类型名称与 {@link #convertFieldValue} 方法中支持的类型对应。
     * </p>
     * <p>
     * 特殊处理：
     * <ul>
     *   <li>SQL Server 的 varchar(max)/nvarchar(max)：columnSize 通常为 2147483647 或 0，映射为 text</li>
     *   <li>MySQL 的 text/longtext：根据 typeName 直接判断</li>
     * </ul>
     * </p>
     *
     * @param jdbcType   JDBC 数据类型（java.sql.Types 常量）
     * @param typeName   数据库特定的类型名称（如 VARCHAR、INT、BIGINT 等）
     * @param columnSize 字段长度（用于区分 varchar 和 varchar(max) 等）
     * @return 标准化的类型名称
     */
    private String mapToStandardType(int jdbcType, String typeName, int columnSize) {
        // SQL Server varchar(max)/nvarchar(max) 的 columnSize 通常为 Integer.MAX_VALUE 或 2147483647
        // 某些驱动可能返回 0 或 -1 表示无限制
        final int MAX_VARCHAR_THRESHOLD = 8000;  // SQL Server varchar 最大长度
        final int MAX_NVARCHAR_THRESHOLD = 4000; // SQL Server nvarchar 最大长度

        // 先根据数据库特定的类型名称进行匹配（更精确）
        if (StringUtils.isNotBlank(typeName)) {
            String typeNameLower = typeName.toLowerCase().trim();

            // 整数类型
            if (typeNameLower.contains("bigint") || typeNameLower.equals("int8")) {
                return "bigint";
            }
            if (typeNameLower.contains("int") || typeNameLower.equals("int4")
                    || typeNameLower.equals("integer") || typeNameLower.equals("mediumint")
                    || typeNameLower.equals("smallint") || typeNameLower.equals("int2")) {
                // tinyint 特殊处理，可能是布尔类型
                if (typeNameLower.equals("tinyint")) {
                    return "tinyint";
                }
                return "int";
            }

            // 浮点数类型
            if (typeNameLower.contains("decimal") || typeNameLower.contains("numeric")
                    || typeNameLower.equals("money") || typeNameLower.equals("smallmoney")) {
                return "decimal";
            }
            if (typeNameLower.contains("double") || typeNameLower.equals("float8")) {
                return "double";
            }
            if (typeNameLower.contains("float") || typeNameLower.equals("real")
                    || typeNameLower.equals("float4")) {
                return "float";
            }

            // 字符串类型
            if (typeNameLower.contains("varchar") || typeNameLower.contains("nvarchar")
                    || typeNameLower.equals("character varying")) {
                // SQL Server: varchar(max) 和 nvarchar(max) 的 columnSize 通常很大或为0/-1
                // 需要区分普通 varchar 和 varchar(max)
                if (typeNameLower.contains("nvarchar")) {
                    // nvarchar(max) 判断：columnSize > 4000 或 columnSize <= 0（表示无限制）
                    if (columnSize > MAX_NVARCHAR_THRESHOLD || columnSize <= 0) {
                        return "text";
                    }
                } else {
                    // varchar(max) 判断：columnSize > 8000 或 columnSize <= 0
                    if (columnSize > MAX_VARCHAR_THRESHOLD || columnSize <= 0) {
                        return "text";
                    }
                }
                return "varchar";
            }
            if (typeNameLower.equals("char") || typeNameLower.equals("nchar")
                    || typeNameLower.equals("character") || typeNameLower.equals("bpchar")) {
                return "char";
            }
            if (typeNameLower.contains("text") || typeNameLower.contains("clob")) {
                if (typeNameLower.equals("text")) {
                    return "text";
                }
                if (typeNameLower.contains("mediumtext")) {
                    return "mediumtext";
                }
                return "longtext";
            }

            // 日期时间类型
            if (typeNameLower.equals("date")) {
                return "date";
            }
            if (typeNameLower.contains("datetime") || typeNameLower.contains("timestamp")) {
                return "datetime";
            }
            if (typeNameLower.contains("time")) {
                return "datetime"; // time类型也用datetime处理
            }

            // 布尔类型
            if (typeNameLower.equals("bit") || typeNameLower.equals("bool")
                    || typeNameLower.equals("boolean")) {
                return "boolean";
            }

            // 二进制类型（暂不特殊处理，返回原值）
            if (typeNameLower.contains("blob") || typeNameLower.contains("binary")
                    || typeNameLower.contains("bytea")) {
                return typeName; // 保持原类型
            }
        }

        // 根据 JDBC 类型常量进行匹配（作为后备方案）
        return switch (jdbcType) {
            // 整数类型
            case Types.BIGINT -> "bigint";
            case Types.INTEGER, Types.SMALLINT -> "int";
            case Types.TINYINT -> "tinyint";

            // 浮点数类型
            case Types.DECIMAL, Types.NUMERIC -> "decimal";
            case Types.DOUBLE -> "double";
            case Types.FLOAT, Types.REAL -> "float";

            // 字符串类型（有长度限制）
            case Types.VARCHAR, Types.NVARCHAR -> "varchar";
            case Types.CHAR, Types.NCHAR -> "char";

            // 长文本类型（无长度限制）
            case Types.LONGVARCHAR, Types.LONGNVARCHAR -> "text";

            // 大对象类型（超大文本）
            case Types.CLOB, Types.NCLOB -> "longtext";

            // 日期时间类型
            case Types.DATE -> "date";
            case Types.TIMESTAMP, Types.TIMESTAMP_WITH_TIMEZONE -> "datetime";
            case Types.TIME, Types.TIME_WITH_TIMEZONE -> "datetime";

            // 布尔类型
            case Types.BOOLEAN, Types.BIT -> "boolean";

            default -> {
                // 未知类型，返回原始类型名称或null
                log.debug("===> 未知JDBC类型: jdbcType={}, typeName={}", jdbcType, typeName);
                yield typeName != null ? typeName : "varchar";
            }
        };
    }

    // ==================== SQL构建方法 ====================

    /**
     * 构建统计SQL
     *
     * @param tableName 表名
     * @param condition WHERE条件（不含WHERE关键字）
     * @return 统计SQL语句
     */
    private String buildCountSql(String tableName, String condition) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT COUNT(1) FROM ").append(tableName);
        if (StringUtils.isNotBlank(condition)) {
            sql.append(" WHERE ").append(condition);
        }
        return sql.toString();
    }

    /**
     * 构建字段规则映射
     * <p>将字段规则列表转换为Map，便于按目标字段名快速查找对应规则</p>
     *
     * @param fieldRules 字段规则列表
     * @return 目标字段名 -> 字段规则 的映射
     */
    private Map<String, FieldRule> buildFieldRuleMap(List<FieldRule> fieldRules) {
        Map<String, FieldRule> map = new HashMap<>(fieldRules.size());
        for (FieldRule rule : fieldRules) {
            map.put(rule.getField(), rule);
        }
        return map;
    }

    /**
     * 构建基础查询SQL（不含分页和排序）
     * <p>
     * 生成的SQL格式：SELECT field1 AS alias1, field2 AS alias2, ... FROM tableName WHERE 1=1 [AND condition]
     * </p>
     *
     * @param sortField  排序字段（用于分页排序，需要查询出来）
     * @param fieldRules 字段规则列表
     * @param tableName  源表名
     * @param condition  WHERE条件（不含WHERE关键字）
     * @return 基础查询SQL
     */
    private String buildBaseQuerySql(String sortField, List<FieldRule> fieldRules,
                                     String tableName, String condition) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT ");

        // 添加字段映射
        List<String> fieldExpressions = new ArrayList<>();
        fieldExpressions.add(sortField);
        for (FieldRule fieldRule : fieldRules) {
            String sourceField = fieldRule.getSourceField();
            String targetField = fieldRule.getField();
            // 使用 AS 别名映射
            fieldExpressions.add(sourceField + " AS " + targetField);
        }

        sql.append(String.join(", ", fieldExpressions));
        sql.append(" FROM ").append(tableName);
        sql.append(" WHERE 1=1");

        if (StringUtils.isNotBlank(condition)) {
            sql.append(" AND ").append(condition);
        }

        return sql.toString();
    }

    /**
     * 构建插入SQL
     * <p>
     * 生成的SQL格式：INSERT INTO tableName (col1, col2, ...) VALUES (?, ?, ...)
     * </p>
     *
     * @param tableName 目标表名
     * @param columns   目标字段列表
     * @return 插入SQL（带占位符）
     */
    private String buildInsertSql(String tableName, List<String> columns) {
        StringBuilder sql = new StringBuilder();
        sql.append("INSERT INTO ").append(tableName).append(" (");
        sql.append(String.join(", ", columns));
        sql.append(") VALUES (");
        sql.append("?,".repeat(columns.size()));
        sql.setLength(sql.length() - 1); // 去掉最后一个逗号
        sql.append(")");
        return sql.toString();
    }

    // ==================== 批量插入方法 ====================

    /**
     * 批量插入数据
     * <p>
     * 使用JDBC的batchUpdate进行批量插入，相比单条插入性能提升显著。
     * 内部按BATCH_SIZE分批执行，避免单次批量过大导致内存溢出或数据库超时。
     * </p>
     *
     * @param jdbcTemplate JDBC模板
     * @param insertSql    插入SQL（带占位符）
     * @param dataList     源数据列表
     * @param columns      目标字段列表（按顺序对应插入SQL的占位符）
     * @param fieldRuleMap 字段规则映射（用于类型转换）
     * @return 实际插入的记录数
     */
    private int batchInsert(JdbcTemplate jdbcTemplate, String insertSql,
                            List<Map<String, Object>> dataList, List<String> columns,
                            Map<String, FieldRule> fieldRuleMap) {

        if (dataList == null || dataList.isEmpty()) {
            return 0;
        }

        // 将数据转换为批量插入参数
        List<Object[]> batchArgs = new ArrayList<>(dataList.size());

        for (Map<String, Object> row : dataList) {
            Object[] params = new Object[columns.size()];
            for (int i = 0; i < columns.size(); i++) {
                String column = columns.get(i);
                Object rawValue = row.get(column);
                FieldRule rule = fieldRuleMap.get(column);

                // 根据字段规则进行类型转换
                params[i] = convertFieldValue(rawValue, rule);
            }
            batchArgs.add(params);
        }

        // 分批执行批量插入
        int totalInserted = 0;
        int totalRecords = batchArgs.size();

        for (int i = 0; i < totalRecords; i += BATCH_SIZE) {
            int endIndex = Math.min(i + BATCH_SIZE, totalRecords);
            List<Object[]> batch = batchArgs.subList(i, endIndex);
            int batchSize = batch.size();

            try {
                // 执行批量插入
                jdbcTemplate.batchUpdate(insertSql, batch);

                // batchUpdate如果执行失败会抛出异常，能执行到这里说明全部成功
                totalInserted += batchSize;

            } catch (Exception e) {
                // 批量插入失败，记录错误但继续处理下一批
                log.error("===> 批量插入失败，批次起始位置：{}，批次大小：{}，错误：{}",
                        i, batchSize, e.getMessage());
            }
        }

        return totalInserted;
    }

    // ==================== 类型转换方法 ====================

    /**
     * 根据字段规则转换字段值
     * <p>
     * 支持的类型转换：
     * <ul>
     *   <li>bigint: 转换为Long</li>
     *   <li>int/integer: 转换为Integer</li>
     *   <li>varchar: 转换为String，并按length截断</li>
     *   <li>text/longtext/mediumtext: 转换为String</li>
     *   <li>date: 转换为java.sql.Date</li>
     *   <li>datetime/timestamp: 转换为java.sql.Timestamp</li>
     *   <li>decimal/numeric: 转换为BigDecimal</li>
     *   <li>double: 转换为Double</li>
     *   <li>float: 转换为Float</li>
     *   <li>boolean/bool/tinyint: 转换为Boolean</li>
     * </ul>
     * </p>
     *
     * @param value 原始值
     * @param rule  字段规则（可为null，为null时返回原值）
     * @return 转换后的值
     */
    private Object convertFieldValue(Object value, FieldRule rule) {
        // 空值直接返回
        if (value == null) {
            return null;
        }

        // 如果没有规则或没有指定类型，返回原值
        if (rule == null || StringUtils.isBlank(rule.getType())) {
            return value;
        }

        String targetType = rule.getType().toLowerCase().trim();

        try {
            return switch (targetType) {
                case "bigint" -> convertToBigint(value);
                case "int", "integer" -> convertToInt(value);
                case "varchar" -> convertToVarchar(value, rule.getLength());
                case "text", "longtext", "mediumtext" -> convertToString(value);
                case "date" -> convertToDate(value);
                case "datetime", "timestamp" -> convertToDatetime(value);
                case "decimal", "numeric" -> convertToDecimal(value);
                case "double" -> convertToDouble(value);
                case "float" -> convertToFloat(value);
                case "boolean", "bool", "tinyint" -> convertToBoolean(value);
                case "char" -> convertToChar(value, rule.getLength());
                default -> value; // 未知类型返回原值
            };
        } catch (Exception e) {
            log.warn("字段值类型转换失败，字段：{}，目标类型：{}，原值类型：{}，原值：{}，错误：{}",
                    rule.getField(), targetType, value.getClass().getSimpleName(), value, e.getMessage());
            return value; // 转换失败返回原值，避免中断整个流程
        }
    }

    /**
     * 转换为Long类型（对应数据库bigint）
     * <p>
     * 注意：BigInteger 是 Number 的子类，会在 Number 分支中处理
     * </p>
     *
     * @param value 原始值
     * @return Long值
     */
    private Long convertToBigint(Object value) {
        if (value instanceof Long) {
            return (Long) value;
        }
        if (value instanceof Boolean) {
            return ((Boolean) value) ? 1L : 0L;
        }
        if (value instanceof Number) {
            // Number 包括 Integer, Short, Byte, Double, Float, BigInteger, BigDecimal 等
            return ((Number) value).longValue();
        }
        if (value instanceof String) {
            String str = ((String) value).trim();
            if (str.isEmpty()) {
                return null;
            }
            // 处理可能的小数点
            if (str.contains(".")) {
                return (long) Double.parseDouble(str);
            }
            return Long.parseLong(str);
        }
        return Long.parseLong(value.toString());
    }

    /**
     * 转换为Integer类型（对应数据库int）
     * <p>
     * 注意：Boolean 不是 Number 的子类，需要单独处理
     * </p>
     *
     * @param value 原始值
     * @return Integer值
     */
    private Integer convertToInt(Object value) {
        if (value instanceof Integer) {
            return (Integer) value;
        }
        if (value instanceof Boolean) {
            return ((Boolean) value) ? 1 : 0;
        }
        if (value instanceof Number) {
            // Number 包括 Long, Short, Byte, Double, Float, BigInteger, BigDecimal 等
            return ((Number) value).intValue();
        }
        if (value instanceof String) {
            String str = ((String) value).trim();
            if (str.isEmpty()) {
                return null;
            }
            // 处理可能的小数点
            if (str.contains(".")) {
                return (int) Double.parseDouble(str);
            }
            return Integer.parseInt(str);
        }
        return Integer.parseInt(value.toString());
    }

    /**
     * 转换为String类型，并根据长度截断（对应数据库varchar）
     *
     * @param value  原始值
     * @param length 最大长度，null或<=0表示使用默认值255
     * @return 字符串值（可能被截断）
     */
    private String convertToVarchar(Object value, Integer length) {
        String str = convertToString(value);
        if (str == null) {
            return null;
        }

        // 默认长度255
        int maxLength = (length != null && length > 0) ? length : 255;

        if (str.length() > maxLength) {
            log.debug("字段值超过最大长度{}，原长度{}，已截断", maxLength, str.length());
            return str.substring(0, maxLength);
        }
        return str;
    }

    /**
     * 转换为定长字符串（对应数据库char）
     *
     * @param value  原始值
     * @param length 固定长度
     * @return 字符串值（可能被截断）
     */
    private String convertToChar(Object value, Integer length) {
        String str = convertToString(value);
        if (str == null) {
            return null;
        }

        int targetLength = (length != null && length > 0) ? length : 1;

        if (str.length() > targetLength) {
            return str.substring(0, targetLength);
        }
        // char类型通常由数据库自动填充空格，这里不做填充
        return str;
    }

    /**
     * 转换为String类型（对应数据库text等）
     *
     * @param value 原始值
     * @return 字符串值
     */
    private String convertToString(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof String) {
            return (String) value;
        }
        if (value instanceof byte[]) {
            return new String((byte[]) value);
        }
        if (value instanceof Clob clob) {
            try {
                return clob.getSubString(1, (int) clob.length());
            } catch (Exception e) {
                log.warn("Clob转换失败：{}", e.getMessage());
                return value.toString();
            }
        }
        return value.toString();
    }

    /**
     * 转换为java.sql.Date类型（对应数据库date）
     * <p>支持多种输入类型：Date、LocalDate、LocalDateTime、Long（时间戳）、String</p>
     *
     * @param value 原始值
     * @return java.sql.Date值
     */
    private java.sql.Date convertToDate(Object value) {
        if (value instanceof java.sql.Date) {
            return (java.sql.Date) value;
        }
        if (value instanceof java.util.Date) {
            return new java.sql.Date(((java.util.Date) value).getTime());
        }
        if (value instanceof LocalDate) {
            return java.sql.Date.valueOf((LocalDate) value);
        }
        if (value instanceof LocalDateTime) {
            return java.sql.Date.valueOf(((LocalDateTime) value).toLocalDate());
        }
        if (value instanceof Long) {
            return new java.sql.Date((Long) value);
        }
        if (value instanceof String) {
            String str = ((String) value).trim();
            if (str.isEmpty()) {
                return null;
            }
            return parseStringToDate(str);
        }
        // 尝试toString后解析
        return parseStringToDate(value.toString());
    }

    /**
     * 转换为java.sql.Timestamp类型（对应数据库datetime/timestamp）
     * <p>支持多种输入类型：Timestamp、Date、LocalDateTime、LocalDate、Long（时间戳）、String</p>
     *
     * @param value 原始值
     * @return Timestamp值
     */
    private Timestamp convertToDatetime(Object value) {
        if (value instanceof Timestamp) {
            return (Timestamp) value;
        }
        if (value instanceof java.util.Date) {
            return new Timestamp(((java.util.Date) value).getTime());
        }
        if (value instanceof LocalDateTime) {
            return Timestamp.valueOf((LocalDateTime) value);
        }
        if (value instanceof LocalDate) {
            return Timestamp.valueOf(((LocalDate) value).atStartOfDay());
        }
        if (value instanceof Long) {
            return new Timestamp((Long) value);
        }
        if (value instanceof String) {
            String str = ((String) value).trim();
            if (str.isEmpty()) {
                return null;
            }
            return parseStringToTimestamp(str);
        }
        // 尝试toString后解析
        return parseStringToTimestamp(value.toString());
    }

    /**
     * 转换为BigDecimal类型（对应数据库decimal/numeric）
     * <p>
     * 类型检查顺序说明：
     * <ul>
     *   <li>BigDecimal: 直接返回，无需转换</li>
     *   <li>BigInteger: 使用构造函数转换，保持精度</li>
     *   <li>Long/Integer: 使用 valueOf(long) 精确转换</li>
     *   <li>Double: 使用 valueOf(double)</li>
     *   <li>其他Number: 使用 doubleValue() 转换（可能损失精度）</li>
     * </ul>
     * </p>
     *
     * @param value 原始值
     * @return BigDecimal值
     */
    private BigDecimal convertToDecimal(Object value) {
        // BigDecimal 直接返回
        if (value instanceof BigDecimal) {
            return (BigDecimal) value;
        }
        // BigInteger 使用构造函数，保持精度
        if (value instanceof BigInteger) {
            return new BigDecimal((BigInteger) value);
        }
        // Long/Integer 使用 valueOf(long)，比 valueOf(double) 更精确
        if (value instanceof Long) {
            return BigDecimal.valueOf((Long) value);
        }
        if (value instanceof Integer) {
            return BigDecimal.valueOf((Integer) value);
        }
        // Double 使用 valueOf(double)
        if (value instanceof Double) {
            return BigDecimal.valueOf((Double) value);
        }
        // 其他 Number 类型（Short, Byte, Float 等）
        if (value instanceof Number) {
            return BigDecimal.valueOf(((Number) value).doubleValue());
        }
        // 字符串解析
        if (value instanceof String) {
            String str = ((String) value).trim();
            if (str.isEmpty()) {
                return null;
            }
            return new BigDecimal(str);
        }
        return new BigDecimal(value.toString());
    }

    /**
     * 转换为Double类型
     *
     * @param value 原始值
     * @return Double值
     */
    private Double convertToDouble(Object value) {
        if (value instanceof Double) {
            return (Double) value;
        }
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }
        if (value instanceof String) {
            String str = ((String) value).trim();
            if (str.isEmpty()) {
                return null;
            }
            return Double.parseDouble(str);
        }
        return Double.parseDouble(value.toString());
    }

    /**
     * 转换为Float类型
     *
     * @param value 原始值
     * @return Float值
     */
    private Float convertToFloat(Object value) {
        if (value instanceof Float) {
            return (Float) value;
        }
        if (value instanceof Number) {
            return ((Number) value).floatValue();
        }
        if (value instanceof String) {
            String str = ((String) value).trim();
            if (str.isEmpty()) {
                return null;
            }
            return Float.parseFloat(str);
        }
        return Float.parseFloat(value.toString());
    }

    /**
     * 转换为Boolean类型（对应数据库boolean/tinyint）
     * <p>
     * 转换规则：
     * <ul>
     *   <li>Boolean类型：直接返回</li>
     *   <li>数值类型：非0为true</li>
     *   <li>字符串："true"/"1"/"yes"/"y" 为true（不区分大小写）</li>
     * </ul>
     * </p>
     *
     * @param value 原始值
     * @return Boolean值
     */
    private Boolean convertToBoolean(Object value) {
        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        if (value instanceof Number) {
            return ((Number) value).intValue() != 0;
        }
        if (value instanceof String) {
            String str = ((String) value).trim().toLowerCase();
            if (str.isEmpty()) {
                return null;
            }
            return "true".equals(str) || "1".equals(str) || "yes".equals(str) || "y".equals(str);
        }
        return false;
    }

    /**
     * 解析字符串为java.sql.Date
     * <p>尝试多种常见日期格式进行解析</p>
     *
     * @param str 日期字符串
     * @return java.sql.Date值
     * @throws IllegalArgumentException 如果无法解析
     */
    private java.sql.Date parseStringToDate(String str) {
        // 优先尝试ISO标准格式 LocalDate解析
        try {
            LocalDate localDate = LocalDate.parse(str);
            return java.sql.Date.valueOf(localDate);
        } catch (DateTimeParseException ignored) {
            // 继续尝试其他格式
        }

        // 尝试多种常见格式
        for (String pattern : DATE_PATTERNS) {
            try {
                SimpleDateFormat sdf = new SimpleDateFormat(pattern);
                sdf.setLenient(false);
                java.util.Date date = sdf.parse(str);
                return new java.sql.Date(date.getTime());
            } catch (ParseException ignored) {
                // 继续尝试下一个格式
            }
        }

        throw new IllegalArgumentException("无法解析日期字符串: " + str);
    }

    /**
     * 解析字符串为Timestamp
     * <p>尝试多种常见日期时间格式进行解析</p>
     *
     * @param str 日期时间字符串
     * @return Timestamp值
     * @throws IllegalArgumentException 如果无法解析
     */
    private Timestamp parseStringToTimestamp(String str) {
        // 优先尝试ISO标准格式 LocalDateTime解析
        try {
            LocalDateTime localDateTime = LocalDateTime.parse(str);
            return Timestamp.valueOf(localDateTime);
        } catch (DateTimeParseException ignored) {
            // 继续尝试其他格式
        }

        // 尝试常用的 yyyy-MM-dd HH:mm:ss 格式
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime localDateTime = LocalDateTime.parse(str, formatter);
            return Timestamp.valueOf(localDateTime);
        } catch (DateTimeParseException ignored) {
            // 继续尝试其他格式
        }

        // 尝试多种常见格式
        for (String pattern : DATE_PATTERNS) {
            try {
                SimpleDateFormat sdf = new SimpleDateFormat(pattern);
                sdf.setLenient(false);
                java.util.Date date = sdf.parse(str);
                return new Timestamp(date.getTime());
            } catch (ParseException ignored) {
                // 继续尝试下一个格式
            }
        }

        throw new IllegalArgumentException("无法解析日期时间字符串: " + str);
    }

    // ==================== 分页SQL构建方法 ====================

    /**
     * 根据不同数据库类型，构建分页SQL
     * <p>
     * 支持的数据库类型：
     * <ul>
     *   <li>MySQL / MariaDB: LIMIT ... OFFSET ...</li>
     *   <li>PostgreSQL: LIMIT ... OFFSET ...</li>
     *   <li>SQL Server: OFFSET ... ROWS FETCH NEXT ... ROWS ONLY</li>
     *   <li>Oracle: ROWNUM子查询方式</li>
     *   <li>达梦(DM): LIMIT ... OFFSET ...</li>
     * </ul>
     * </p>
     *
     * @param dbType       数据库类型
     * @param srcSortField 排序字段
     * @param srcSortType  排序方式（0升序ASC，1降序DESC）
     * @param baseSql      基础查询SQL（不含ORDER BY和分页）
     * @param offset       偏移量（从第几条开始）
     * @param pageSize     页大小（每页多少条）
     * @return 完整的分页查询SQL
     * @throws BusinessException 如果数据库类型不支持
     */
    private String buildPaginatedSql(String dbType, String srcSortField, Integer srcSortType,
                                     String baseSql, int offset, int pageSize) {

        // 排序字段，默认id
        String sortField = StringUtils.isNotBlank(srcSortField) ? srcSortField : "id";
        // 排序方式，默认升序
        String sortOrder = (srcSortType != null && srcSortType == 1) ? "DESC" : "ASC";

        // 根据不同数据库类型构建分页语句
        String dbTypeLower = dbType.toLowerCase();

        return switch (dbTypeLower) {
            case "mysql", "mariadb" -> baseSql + " ORDER BY " + sortField + " " + sortOrder
                    + " LIMIT " + pageSize + " OFFSET " + offset;

            case "postgresql", "postgres", "kingbase" -> baseSql + " ORDER BY " + sortField + " " + sortOrder
                    + " LIMIT " + pageSize + " OFFSET " + offset;

            case "sqlserver", "mssql" -> baseSql + " ORDER BY " + sortField + " " + sortOrder
                    + " OFFSET " + offset + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY";

            case "oracle" ->
                // Oracle 12c之前的写法，兼容性更好
                    "SELECT * FROM (SELECT t.*, ROWNUM rn FROM (" + baseSql
                            + " ORDER BY " + sortField + " " + sortOrder
                            + ") t WHERE ROWNUM <= " + (offset + pageSize)
                            + ") WHERE rn > " + offset;

            case "dm", "dameng" ->
                // 达梦数据库
                    baseSql + " ORDER BY " + sortField + " " + sortOrder
                            + " LIMIT " + pageSize + " OFFSET " + offset;

            default -> throw new BusinessException("不支持的数据库类型：" + dbType);
        };
    }
}